home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************************
-
- Originally "power.c" (c) manfredo 9/91 (manfredo@opal.cs.tu-berlin.de)
- Developed on an ATARI 1040ST with TC 1.1 using a logic analyzer to get
- the correct timings.
-
- **********************************************************************/
- /*********************************************************************
- ported to PC compatibles by
- Greg Alt 10/91
-
- galt@peruvian.utah.edu
- or galt@es.dsd.com
-
- **********************************************************************/
- /*********************************************************************
-
- Substantially rewritten by Dave Stampe (c) 1991: PWRFILT.C
- No cash, no warranty, no flames.
- This stuff works great, so gimme credit.
-
- Goals <achieved> were:
-
- Higher speed, smaller code.
- Polled operation is now possible.
- Graphics test (VGA)
- Noise reduction added, gets rid of 99.5% of noise with NO DELAY!
-
- This runs on a 486/25 with an i/o card.
- Someone should adapt it for the usual printer port adapter.
- It was compiled with Turbo C++ 2.0 but will probably
- work on any Turbo C directly. MSC will need library calls checked.
-
-
- dstamp@watserv1.uwaterloo.ca 17/10/91
- **********************************************************************/
-
- /*********************************************************************
- Re-converted to use printer port by Dave Stampe and Bernie Roehl.
- October 18, 1991.
-
- I also split off Dave's graphics code into a separate file, and put some
- of the stuff that was shared between the two into glove.h
-
- I also added a little judicious whitespace here and there to enhance
- readability, and made a whole bunch of globals static.
-
- I also added init_glove(mode) and glove_read(glov), the latter of which
- calls getglove(glov), deglitch(glov) and dehyst(glov).
-
- --Bernie, October 18-19 1991
- ********************************************************************/
-
- /* More changes:
-
- init_glove() now auto-calibrates. A new function (available outside
- of this module) called "udelay" delays for a certain number of micro-
- seconds. Calls to fdelay have been replaced by udelay(),
- and the D2BITS and D2BYTES values are now in microseconds.
-
- init_glove() now takes an additional parameter, a pointer to a
- function (currently unused).
-
- --Bernie Roehl, October 21 1991
- */
-
- /* Interrupt-driven operation is now implemented! Simply specify IHIRES
- (for interrupt-driven high-resolution mode).
-
- Also, based on suggestions by Dave Stampe, I've:
- - put the calibration stuff into a separate routine
- - gone back to using counts instead of microseconds in most
- places (for performance reasons; it saves a MULT instruction);
- - udelay() is now gone, and a uconvert() macro does the conversion
-
- I have also:
- - renamed the functions so they all begin with "glove_"; they
- are now named glove_init(), glove_read() and glove_quit()
-
- Many thanks to Dave for all his help, especially in finding some
- annonying timing problems.
-
- -- Bernie Roehl, November 8 1991
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <dos.h>
- #include <bios.h>
- #include <signal.h>
-
- #include "glove.h"
-
- #define PC_PRINTER 1 /* use the PC Printer port for i/o */
-
- #define D2BITS 4 /* microseconds */
- #define D2BYTES 96 /* microseconds */
- #define SAMPLE_TIME 20 /* milliseconds */
-
- #ifdef PC_PRINTER
- #define INPORT 0x379 /* i/o port addresses */
- #define OUTPORT 0x378
- /* bits from parallel port */
- #define GDATA 0x10 /* PG data in */
- #define GLATCH 0x02 /* PG latch out */
- #define GCLOCK 0x01 /* PG clock out */
- #define GCLOLAT 0x03 /* clock + latch */
- #define SHIFTVAL 4 /* shift data right 4 bits */
- #endif
-
- #ifdef DSTAMPE /* stuff from here down to #else is i/o card-specific */
- #define INPORT 0x2A0 /* i/o port addresses */
- #define OUTPORT 0x2A0
- /* bits for i/o ports */
- #define GDATA 0x01 /* PG data in */
- #define GLATCH 0x02 /* PG latch out */
- #define GCLOCK 0x01 /* PG clock out */
- #define GCLOLAT 0x03 /* clock + latch */
- #define SHIFTVAL 0 /* don't shift */
- #endif
-
- static unsigned long bitdelay, bytedelay, longdelay;
-
- static void fdelay(unsigned long val)
- {
- while (val--);
- }
-
- static unsigned long microfactor = 0L; /* usec/iteration times 91 */
-
- #define uconvert(microseconds) (((microseconds) * microfactor)/91)
-
- void glove_delay()
- {
- fdelay(longdelay);
- }
-
- /* defines for output line pair control */
-
- #define C0L0() outportb(OUTPORT, 0) /* clock 0 latch 0 */
- #define C0L1() outportb(OUTPORT, GLATCH) /* clock 0 latch 1 */
- #define C1L0() outportb(OUTPORT, GCLOCK) /* clock 1 latch 0 */
- #define C1L1() outportb(OUTPORT, GCLOLAT) /* clock 1 latch 1 */
-
- static unsigned char getbyte() /* read a byte from glove <rolled code> */
- {
- register int i;
- register unsigned char x = 0;
-
- C1L0(); /* generate a reset (latch) pulse */
- C1L1();
- fdelay(bitdelay); /* hold for 3 us */
- C1L0();
- fdelay(bitdelay); /* hold for 3 us */
-
- for(i = 0; i < 8; i++)
- {
- x = (x << 1) + ((inportb(INPORT) & GDATA) >> SHIFTVAL);
- C0L0();
- fdelay(bitdelay);
- C1L0(); /* pulse */
- fdelay(bitdelay);
- }
- return x; /* return the byte */
- }
-
- void getglove(glove_data *buf) /* read 6 byte data packet */
- {
- register unsigned char *bp = (char *) buf;
- register int i;
- for (i = 0; i < 6; ++i) {
- *bp++ = getbyte(); /* read data */
- fdelay(bytedelay);
- }
- /* throwaways (speeds up polling later) */
- getbyte();
- fdelay(bytedelay);
- getbyte();
- }
-
- /* HIRES ENTRY CODES
- byte:
- 1- any value between $05 and $31
- 2- only $C1 and $81 work OK
- 3- no effect
- 4- no effect
- 5- no effect
- 6- only $FF works
- 7- seems to affect read rate slightly, 1 fastest
- */
-
- static int hires_code[7] = { 0x06, 0xC1, 0x08, 0x00, 0x02, 0xFF, 0x01 };
-
- void Hires() /* enter HIRES mode <rolled code- speed unimportant> */
- {
- int i,j,k;
- /* dummy read 4 bits from glove: */
- C1L0(); C1L1(); /* generate a reset (latch) pulse */
- fdelay(bitdelay); /* delay for 6 us */
- fdelay(bitdelay);
- C1L0();
- fdelay(bitdelay); /* delay for 6 us */
- fdelay(bitdelay);
-
- C0L0(); C1L0(); /* pulse clock */
- fdelay(bitdelay);
- C0L0(); C1L0(); /* pulse clock */
- fdelay(bitdelay);
- C0L0(); C1L0(); /* pulse clock */
- fdelay(bitdelay);
- C0L0(); C1L0(); /* pulse clock */
-
- /* handshake for command code? */
- C1L0();
- fdelay(uconvert(7212)); /* 7212 us delay */
- C1L1();
- fdelay(uconvert(2260)); /* 2260 us delay */
-
- for (i = 0; i < 7; i++) /* send 7 bytes */
- {
- k = hires_code[i];
- for (j = 0; j < 8; j++) /* 8 bits per byte, MSB first */
- {
- if (k & 0x80)
- {
- C1L1();
- C0L1();
- C1L1();
- }
- else
- {
- C1L0();
- C0L0();
- C1L0();
- }
- k <<= 1;
- fdelay(bitdelay);
- }
- fdelay(bytedelay);
- }
-
- fdelay(uconvert(892)); /* 892 us delay (end of 7. byte) */
-
- C1L0(); /* drop the reset line */
- fdelay(uconvert(40000)); /* some time for the glove controller to relax */
- }
-
- #define XHYST 2 /* hysterisis for X, Y low noise reduction */
- #define YHYST 2 /* 2 eliminates +/-3 quanta of noise */
-
- static int ox = -1000; /* last x,y for hysterisis */
- static int oy = -1000;
-
- static void dehyst(glove_data *g) /* hysterisis deglitch (low noise removal) */
- {
- int x = g->x;
- int y = g->y;
-
- if(g->keys==0) ox = oy = 0; /* handle recentering ("0"key or "Center") */
-
- if(x-ox>XHYST) ox = x-XHYST; /* X hysterisis */
- if(ox-x>XHYST) ox = x+XHYST;
-
- if(y-oy>YHYST) oy = y-YHYST; /* Y hysterisis */
- if(oy-y>YHYST) oy = y+YHYST;
-
- g->x = ox; /* replace present X,Y data */
- g->y = oy;
- }
-
- #define XACC 8 /* X, Y maximum accel/decel level. Should */
- #define YACC 8 /* be 6-10, but too high limits gesturing */
-
- #define XXTEND 1 /* stretches deglitching time */
- #define YXTEND 1
-
- static int x1 = 0; /* delayed 1 sample (for smoothed velocity test) */
- static int y1 = 0;
- static int x2 = 0; /* delayed 2 samples */
- static int y2 = 0;
- static int lx = 0; /* last good X,Y speed */
- static int ly = 0;
- static int lax = 0; /* bad data "stretch" counter */
- static int lay = 0;
- static int lsx = 0; /* X,Y "hold" values to replace bad data */
- static int lsy = 0;
- static int lcx = 0; /* last X,Y speed for accel. calc. */
- static int lcy = 0;
-
- static void deglitch(glove_data *g)
- {
- int vx, vy;
-
- int x = g->x;
- int y = g->y;
-
- if(g->keys == 0) /* reset on recentering ("0" or "Center" key) */
- {
- x1 = x2 = y1 = y2 = 0;
- lx = ly = lax = lay = 0;
- lsx = lsy = lcx = lcy = 0;
- }
-
- vx = x-((x1+x2)>>1); /* smoothed velocity */
- vy = y-((y1+y2)>>1);
-
- x2 = x1; /* update last values */
- x1 = g->x;
-
- y2 = y1;
- y1 = g->y;
-
- if (abs(lcx-vx) > XACC) lax = XXTEND; /* check for extreme acceleration */
- if (lax == 0) lx = vx; /* save only good velocity */
- lcx = vx; /* save velocity for next accel. */
-
- if (abs(lcy-vy) > YACC) lay = YXTEND; /* same deal for Y accel. */
- if (lay == 0) ly = vy;
- lcy = vy;
-
- if (lax != 0) /* hold X pos'n if glitch */
- {
- g->x = lsx;
- lax--;
- }
-
- if (lay != 0) /* hold Y pos'n if glitch */
- {
- g->y = lsy;
- lay--;
- }
-
- lsx = g->x; /* save position for X,Y hold */
- lsy = g->y;
-
- /* g->y = x;*/
- }
-
- static void calibrate()
- {
- unsigned long n;
- /* calibrate timing loop; note that instead of dividing by 18.2,
- we multiply by 5 now and divide by 91 later */
- n = biostime(0, 0L);
- fdelay(1000000); /* divide by a milllion to get microfactor */
- microfactor = (biostime(0, 0L) - n) * 5;
- bitdelay = uconvert(D2BITS);
- bytedelay = uconvert(D2BYTES);
- longdelay = uconvert(4000);
- }
-
- static int glovemode = HIRES; /* operating mode of glove */
- static void (*upcall)() = NULL; /* user's function to call up to */
- static unsigned char uses_ints = 0; /* non-zero if we're interrupt-driven */
- static glove_data glove_int_data; /* our copy of the most recent data */
-
- /* Following is the number of our ticks per real tick, rounded up */
- #define DIVISOR ((55+SAMPLE_TIME/2)/SAMPLE_TIME)
-
- #define UNUSED_VECT 128 /* unused interrupt vector (we hope!) */
-
- static void interrupt (*oldint8)() = NULL; /* previous interrupt handler */
- static void interrupt (*oldunused)() = NULL; /* previous unused vector */
-
- static unsigned n_ints = 0; /* number of interrupts since oldint8 called */
- static unsigned unready = 0; /* number of times glove has been not ready */
-
- static char samples[7500]; /* samples of X values */
- static char dsamples[7500]; /* samples of X values after deglitching */
- static int nsamples = 0;
-
- static void interrupt int8()
- {
- disable();
- if (getbyte() != 0xA0) {
- if (++unready > 500) { /* glove not responding... reset it */
- unready = 0;
- if (glovemode == IHIRES)
- Hires();
- }
- }
- else { /* data ready! */
- fdelay(bytedelay);
- unready = 0;
- getglove(&glove_int_data);
- deglitch(&glove_int_data); /* remove spikes and jumps */
- dehyst(&glove_int_data); /* add hysteresis to remove LL noise */
- ++glove_int_data.nmissed; /* flag data as new */
- if (upcall) (*upcall)(); /* upcall to application */
- }
- if (++n_ints >= DIVISOR) { /* call previous int 8 handler periodically */
- n_ints = 0;
- geninterrupt(UNUSED_VECT);
- }
- else
- outportb(0x20, 0x20); /* signal EOI */
- }
-
- #define TIMER_CONTROL 0x43 /* timer control register */
- #define TIMER_0 0x40 /* timer zero data register */
- #define TIMER_MODE 0x36 /* byte to write to control register */
-
- int glove_init(int mode, void (*function)())
- {
- calibrate();
- if (mode == LORES) mode = HIRES;
- if (mode == ILORES) mode = IHIRES;
- if (mode == HIRES || mode == IHIRES) Hires();
- glove_int_data.nmissed = 0; /* go this one, start counter again! */
- if (mode == ILORES || mode == IHIRES) { /* an interrupt mode */
- void glove_quit();
- unsigned rate;
- uses_ints = 1;
- oldint8 = getvect(8);
- oldunused = getvect(UNUSED_VECT);
- setvect(UNUSED_VECT, oldint8);
- setvect(8, int8);
- rate = 65535/DIVISOR;
- /* reprogram timer */
- disable();
- outportb(TIMER_CONTROL, TIMER_MODE); /* timer control port */
- outportb(TIMER_0, rate & 0xFF); /* low byte to timer zero */
- outportb(TIMER_0, (rate >> 8) & 0xFF); /* high byte to timer zero */
- /* make sure we clean up at exit or on errors */
- atexit(glove_quit);
- signal(SIGABRT, glove_quit);
- signal(SIGFPE, glove_quit);
- signal(SIGINT, glove_quit);
- enable();
- }
- else
- uses_ints = 0;
- glovemode = mode;
- upcall = function;
- return glovemode; /* returns mode actually set */
- }
-
- int glove_read(glove_data *glov)
- {
- if (uses_ints) { /* interrupt-driven */
- disable();
- *glov = glove_int_data;
- glove_int_data.nmissed = 0;
- enable();
- return glov->nmissed;
- }
- /* polled operation */
- getglove(glov); /* retrieve the data */
- deglitch(glov); /* remove spikes and jumps */
- dehyst(glov); /* add hysteresis to remove LL noise */
- glove_int_data = *glov;
- return 1; /* always new data */
- }
-
- int glove_ready() /* returns 1 if glove ready, 0 otherwise */
- {
- if (uses_ints) /* interrupt handler makes sure there's always data */
- return 1;
- if (getbyte() == 0xA0) { /* ready */
- unready = 0;
- if (upcall) (*upcall)();
- return 1;
- }
- /* not ready */
- if (++unready > 500) { /* glove not responding... reset it */
- unready = 0;
- if (glovemode == HIRES || glovemode == IHIRES)
- Hires();
- }
- return 0;
- }
-
- void glove_quit()
- {
- if (uses_ints) {
- disable();
- /* reprogram timer */
- outportb(TIMER_CONTROL, TIMER_MODE);
- outportb(TIMER_0, 0); /* low byte */
- outportb(TIMER_0, 0); /* high byte */
- setvect(8, oldint8);
- setvect(UNUSED_VECT, oldunused);
- signal(SIGABRT, SIG_DFL);
- signal(SIGFPE, SIG_DFL);
- signal(SIGINT, SIG_DFL);
- enable();
- }
- upcall = NULL;
- }
-
-